﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using MySql.Data.MySqlClient;

namespace MySqlManager.DatabaseBuilder
{
    /// <summary>
    /// Layer class over a MySqlCommand to create a new MySQL table
    /// </summary>
    public class MySqlTableCreator
    {
        /// <summary>
        /// True if the creation will be executed in a thread of the ThreadPool
        /// </summary>
        public bool CreateAsync = false;

        /// <summary>
        /// Contains all the columns formatted as query that will contain the table
        /// </summary>
        private List<string> columnsCollection;

        /// <summary>
        /// The MySQL query builder containing the command text that will be executed
        /// </summary>
        private StringBuilder query;
        
        /// <summary>
        /// Gets or sets the Type of the class that will be created as table
        /// </summary>
        private Type classType;

        /// <summary>
        /// Methods that will be invoked once the table creation finishes
        /// </summary>
        public event Action Comleted;

        /// <summary>
        /// Gets or sets the name that will have the new Table
        /// </summary>
        private string TableName { get; set; }

        /// <summary>
        /// Gets or sets the Primary Key of the new table
        /// </summary>
        private string PrimaryKey { get; set; }

        /// <summary>
        /// Gets or sets the initial value for the AutoIncrement of the new table
        /// </summary>
        private int AutoIncrementerStart { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="MySqlTableCreator"/> class
        /// </summary>
        public MySqlTableCreator()
        {
            this.columnsCollection = new List<string>();
        }

        /// <summary>
        /// If no table exists with the specified name, create it
        /// </summary>
        /// <typeparam name="T">Type containing the information of the table</typeparam>
        public void CreateIfNotExistTableFrom<T>()
        {
            this.classType = typeof(T);
            if (this.CreateAsync)
            {
                Action action = this.GetTableColumns;
                action.BeginInvoke((IAsyncResult result) =>
                    {
                        action.EndInvoke(result);
                        if (this.Comleted != null)
                        {
                            this.Comleted.Invoke();
                        }
                    },
                    null);
            }
            else
            {
                this.GetTableColumns();
                if (this.Comleted != null)
                {
                    this.Comleted.Invoke();
                }
            }
        }

        /// <summary>
        /// Gets all the information required for the new table columns
        /// from the specified class
        /// </summary>
        private void GetTableColumns()
        {
            //getting the attributes of the class
            foreach (object atr in this.classType.GetCustomAttributes(true))
            {
                MySqlTableAttribute attribute = atr as MySqlTableAttribute;
                if (attribute != null)
                {
                    this.TableName = attribute.TableName;
                    this.AutoIncrementerStart = attribute.AutoIncrementerStart;
                }
            }

            //Getting all the properties of the class
            foreach (PropertyInfo propertyInfo in this.classType.GetProperties())
            {
                foreach (object atr in propertyInfo.GetCustomAttributes(true))
                {
                    var attribute = atr as MySqlFieldAttribute;
                    if (attribute != null)
                    {
                        if (attribute.IsPrimaryKey)
                        {
                            this.PrimaryKey = propertyInfo.Name;
                        }

                        attribute.VariableType = propertyInfo.PropertyType;
                        this.columnsCollection.Add(attribute.ToString());
                    }
                }
            }

            //getting all the fields in the class
            foreach (FieldInfo fieldInfo in this.classType.GetFields())
            {
                foreach (object atr in fieldInfo.GetCustomAttributes(true))
                {
                    var attribute = atr as MySqlFieldAttribute;
                    if (attribute != null)
                    {
                        if (attribute.IsPrimaryKey)
                        {
                            this.PrimaryKey = fieldInfo.Name;
                        }

                        attribute.VariableType = fieldInfo.FieldType;
                        this.columnsCollection.Add(attribute.ToString());
                    }
                }
            }

            this.ExecuteQuery();
        }

        /// <summary>
        /// Creates and execute the query that will create the table
        /// </summary>
        private void ExecuteQuery()
        {
            this.query = new StringBuilder("Create Table If Not Exists ");
            this.query.Append(this.TableName);
            this.query.Append(" (");
            this.query.Append(string.Join(",", this.columnsCollection));

            if (this.PrimaryKey != null)
            {
                this.query.Append(", PRIMARY KEY ");
                this.query.AppendFormat("(`{0}`)", this.PrimaryKey);
            }

            this.query.Append(")");
            if (this.AutoIncrementerStart != int.MaxValue)
            {
                this.query.Append(" AUTO_INCREMENT = ").Append(this.AutoIncrementerStart);
                this.query.Append(",ENGINE=InnoDB;");
            }
            else
            {
                this.query.Append("ENGINE=InnoDB");
            }

            using (MySqlCommand cmd = new MySqlCommand(this.query.ToString()))
            {
                using (cmd.Connection = MySqlCore.GetConnection())
                {
                    cmd.ExecuteNonQuery();
                }
            }
        }
    }
}